use super::{
    err, err_value, json, throw_err, Base, Collect, ECode, KeyNode, Resp, SStr, VBase, VMap, Value,
    VecS, VecSv, VecV,
};
use std::collections::{HashMap, HashSet};

pub struct Level<'a> {
    tp: &'a str,
    coll: &'a Collect<'a>,
}

impl Base for Level<'_> {}

impl<'a> Level<'a> {
    pub fn new(tp: &'a str, coll: &'a Collect<'a>) -> Self {
        Level { tp, coll }
    }

    pub fn add_vec_to_year(&self, y: &str, val: &VecV, put: &mut VecSv) -> Result<(), Value> {
        let k_all = self.key_all()?;
        let k_y = self.key_year(y)?;

        self.coll.merge_mass(&k_all, y, put);
        let _ = self.coll.merge_mass_vec(&k_y, val, put);

        Ok(())
    }

    pub fn add_sn_to_year(&self, y: &str, sn: &str, put: &mut VecSv) -> Result<(), Value> {
        let k_all = self.key_all()?;
        let k_y = self.key_year(y)?;

        self.coll.merge_mass(&k_all, y, put);
        self.coll.merge_mass(&k_y, sn, put);

        Ok(())
    }

    pub fn add_sn_to_day(&self, tm: &str, sn: &str, put: &mut VecSv) -> Result<(), Value> {
        let (y, m, d) = self.key_ymd(tm)?;

        let k_all = self.key_all()?;
        let k_y = self.key_year(&y)?;
        let k_m = self.key_month(&y, &m)?;
        let k_d = self.key_day(&y, &m, &d)?;

        self.coll.merge_mass(&k_all, &y, put);
        self.coll.merge_mass(&k_y, &m, put);
        self.coll.merge_mass(&k_m, &d, put);
        self.coll.merge_mass(&k_d, sn, put);

        Ok(())
    }

    pub async fn list_year(&self) -> Result<VecV, Value> {
        let k_a = self.key_all()?;
        let body = self.coll.content(&k_a).await?;

        let mut ret = Vec::new();

        for y in body.iter() {
            let k_y = self.key_year(y)?;

            if let Ok(mut val) = self.coll.list_array(&k_y, self.tp, None).await {
                if let Some(v) = val.as_array_mut() {
                    ret.append(v);
                }
            }
        }

        if ret.is_empty() {
            err!(ECode::EmptyData, "no data")
        } else {
            Ok(ret)
        }
    }

    pub async fn list_day(&self, s: &str, e: &str, count: bool) -> Resp {
        let (s_y, s_m, s_d) = self.key_ymd(s)?;
        let (e_y, e_m, e_d) = self.key_ymd(e)?;

        let k_a = self.key_all()?;
        let body_a = self.coll.content(&k_a).await?;

        let mut all_md = Vec::new();
        for y in body_a.iter() {
            if *y < s_y || *y > e_y {
                continue;
            }

            let k_y = self.key_year(y)?;
            let body_y = self.coll.content(&k_y).await?;

            for m in body_y.iter() {
                let k_m = self.key_month(y, m)?;
                let body_m = self.coll.content(&k_m).await?;

                for d in body_m.iter() {
                    all_md.push((
                        format!("{y}{m}{d}"),
                        y.to_string(),
                        m.to_string(),
                        d.to_string(),
                    ));
                }
            }
        }

        let s = format!("{s_y}{s_m}{s_d}");
        let e = format!("{e_y}{e_m}{e_d}");

        let mut ret = Vec::new();

        for d in all_md.iter() {
            if d.0 < s || d.0 > e {
                continue;
            }

            let k_d = self.key_day(&d.1, &d.2, &d.3)?;

            if let Ok(mut val) = self.coll.list_array(&k_d, self.tp, None).await {
                if let Some(v) = val.as_array_mut() {
                    ret.append(v);
                }
            }
        }

        if ret.is_empty() {
            throw_err!(ECode::EmptyData, "no data");
        }

        if !count {
            return Ok(json!(ret));
        }

        let mut val = Vec::new();

        for d in ret.iter() {
            let d = d.ckobj()?;
            let sn = d.ck_str("sn")?;

            let k_psn = self.coll.key_sub_sn("task", "psn", sn)?;
            let mut standout = 0.0;
            let mut len = 0;

            let num = match self.coll.list_array(&k_psn, "psn", None).await {
                Ok(v) => match v.as_array() {
                    Some(t) => {
                        for s in t.iter() {
                            if let Some(s) = s.k_f64("standout") {
                                standout += s;
                                len += 1;
                            }
                        }

                        t.len()
                    }
                    None => 0,
                },
                Err(_) => 0,
            };

            let mut tmp = d.clone();
            standout = if len == 0 { 0.0 } else { standout / len as f64 };

            tmp.insert("psn_num".to_string(), json!(num));
            tmp.insert("standout".to_string(), json!(format!("{:.3}", standout)));

            val.push(tmp);
        }

        Ok(json!(val))
    }

    pub async fn rm_year_set(
        &self,
        set: &SStr,
        put: &mut VecSv,
        del: &mut VecS,
    ) -> Result<(), Value> {
        let mut del_y = HashMap::new();

        for sn in set.iter() {
            let k_item = match self.key_item(sn) {
                Ok(v) => v,
                Err(_) => {
                    continue;
                }
            };

            let item = match self.coll.item(&k_item).await {
                Ok(v) => v,
                Err(_) => {
                    continue;
                }
            };

            let pstart = item.ck_str("pstart")?;
            let (y, _, _) = self.key_ymd(pstart)?;

            let k_y = match self.key_year(&y) {
                Ok(v) => v,
                Err(_) => {
                    continue;
                }
            };

            del_y
                .entry(k_y.to_string())
                .or_insert(SStr::new())
                .insert(sn.to_string());

            del.push(k_item);
        }

        for (k, v) in del_y.iter() {
            self.coll.del_mass_set(k, v, false, put, del);
        }

        Ok(())
    }

    pub async fn rm_year_item(
        &self,
        sn: &str,
        put: &mut VecSv,
        del: &mut VecS,
    ) -> Result<(), Value> {
        let k_item = self.key_item(sn)?;

        let item = match self.coll.item(&k_item).await {
            Ok(v) => v,
            Err(_) => {
                return Ok(());
            }
        };

        let pstart = item.ck_str("pstart")?;
        let (y, _, _) = self.key_ymd(pstart)?;

        let k_a = self.key_all()?;
        let k_y = self.key_year(&y)?;

        del.push(k_item);

        let is_del = self.coll.del_mass_item(&k_y, sn, put, del);
        if !is_del {
            return Ok(());
        }

        self.coll.del_mass_item(&k_a, &y, put, del);

        Ok(())
    }

    pub async fn rm_day_item(
        &self,
        sn: &str,
        put: &mut VecSv,
        del: &mut VecS,
    ) -> Result<(), Value> {
        let k_item = self.key_item(sn)?;

        let item = match self.coll.item(&k_item).await {
            Ok(v) => v,
            Err(_) => {
                return Ok(());
            }
        };

        let pstart = item.ck_str("pstart")?;
        let (y, m, d) = self.key_ymd(pstart)?;

        let k_a = self.key_all()?;
        let k_y = self.key_year(&y)?;
        let k_m = self.key_month(&y, &m)?;
        let k_d = self.key_day(&y, &m, &d)?;

        del.push(k_item);

        let is_del = self.coll.del_mass_item(&k_d, sn, put, del);
        if !is_del {
            return Ok(());
        }

        let is_del = self.coll.del_mass_item(&k_m, &d, put, del);
        if !is_del {
            return Ok(());
        }

        let is_del = self.coll.del_mass_item(&k_y, &m, put, del);
        if !is_del {
            return Ok(());
        }

        self.coll.del_mass_item(&k_a, &y, put, del);

        Ok(())
    }

    pub fn md_day_item(
        &self,
        s: &str,
        n_s: &str,
        sn: &str,
        put: &mut VecSv,
        del: &mut VecS,
    ) -> Result<(), Value> {
        let (y, m, d) = self.key_ymd(s)?;
        let (ny, nm, nd) = self.key_ymd(n_s)?;

        if y == ny && m == nm && d == nd {
            return Ok(());
        }

        let k_all = self.key_all()?;
        let k_y = self.key_year(&ny)?;
        let k_m = self.key_month(&ny, &nm)?;
        let k_d = self.key_day(&ny, &nm, &nd)?;

        let k_old_d = self.key_day(&y, &m, &d)?;

        self.coll.merge_mass(&k_all, &ny, put);
        self.coll.merge_mass(&k_y, &nm, put);
        self.coll.merge_mass(&k_m, &nd, put);
        self.coll.merge_mass(&k_d, sn, put);

        self.coll.del_mass_item(&k_old_d, sn, put, del);

        Ok(())
    }
}

impl<'a> Level<'a> {
    pub async fn rm_month(&self, tm: &str, put: &mut VecSv, del: &mut VecS) -> Result<SStr, Value> {
        let tmp = format!("{tm} 00:00:00");
        let (y, m, _) = self.key_ymd(&tmp)?;

        let k_a = self.key_all()?;
        let k_y = self.key_year(&y)?;
        let k_m = self.key_month(&y, &m)?;

        let mut item_set = HashSet::new();

        let body_m = match self.coll.content(&k_m).await {
            Ok(v) => v,
            Err(_) => {
                return Ok(item_set);
            }
        };

        for d in body_m.iter() {
            let k_d = self.key_day(&y, &m, d)?;

            if let Ok(body) = self.coll.content(&k_d).await {
                for k in body.iter() {
                    let k_item = self.key_item(k)?;

                    del.push(k_item);
                    item_set.insert(k.to_string());
                }
            }

            del.push(k_d);
        }

        del.push(k_m);

        let is_del = self.coll.del_mass_item(&k_y, &m, put, del);
        if is_del {
            self.coll.del_mass_item(&k_a, &y, put, del);
        }

        Ok(item_set)
    }
}

impl<'a> Level<'a> {
    fn key_all(&self) -> Result<String, Value> {
        self.coll.key_all(self.tp)
    }

    fn key_year(&self, y: &str) -> Result<String, Value> {
        self.coll.key_sub_sn(self.tp, "year", y)
    }

    fn key_month(&self, y: &str, m: &str) -> Result<String, Value> {
        let k = format!("{y}{m}");
        self.coll.key_sub_sn(self.tp, "month", &k)
    }

    fn key_day(&self, y: &str, m: &str, d: &str) -> Result<String, Value> {
        let k = format!("{y}{m}{d}");
        self.coll.key_sub_sn(self.tp, "day", &k)
    }

    fn key_item(&self, sn: &str) -> Result<String, Value> {
        self.coll.key_sn(self.tp, sn)
    }

    fn key_ymd(&self, pstart: &str) -> Result<(String, String, String), Value> {
        let pstart = self.from_iso(pstart)?;

        let y = pstart.format("%Y").to_string();
        let m = pstart.format("%m").to_string();
        let d = pstart.format("%d").to_string();

        Ok((y, m, d))
    }
}
